use askama::Template; // 引入 Template trait
// 1. 修改引入部分，去掉 Html
use axum::{
    extract::{Form, Path, State},
    http::StatusCode,
    response::IntoResponse, // 去掉了 Html
    routing::{get, post},
    Router,
};
use serde::Deserialize;
use sqlx::{sqlite::SqlitePoolOptions, FromRow, SqlitePool};
use std::net::SocketAddr;

// --- 1. 领域模型 ---

#[derive(FromRow, Clone)]
struct Todo {
    id: i64,
    title: String,
    completed: bool,
}

// 用于接收前端表单数据
#[derive(Deserialize)]
struct CreateTodo {
    title: String,
}

// --- 2. 模板定义 (Askama) ---

#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {
    todos: Vec<Todo>,
}

// 2. 修改 TodoTemplate 定义
#[derive(Template)]
#[template(path = "todo.html")]
struct TodoTemplate {
    // 以前是分散的字段，现在包裹在 todo 里面，
    // 这样模板里的 {{ todo.id }} 才能正常工作
    todo: Todo,
}

// 将 DB 模型转换为模板模型的方法
// 3. 修改 From 实现
impl From<Todo> for TodoTemplate {
    fn from(todo: Todo) -> Self {
        Self { todo }
    }
}

// 首页：获取所有 Todos 并渲染完整页面
async fn index(State(pool): State<SqlitePool>) -> impl IntoResponse {
    let todos = sqlx::query_as::<_, Todo>("SELECT * FROM todos ORDER BY id DESC")
        .fetch_all(&pool)
        .await
        .unwrap_or_default();

    IndexTemplate { todos }
}

// 添加：插入 DB，返回单个 Todo 的 HTML 片段 (HTMX 将其追加到列表中)
async fn add_todo(
    State(pool): State<SqlitePool>,
    Form(form): Form<CreateTodo>,
) -> impl IntoResponse {
    let id = sqlx::query("INSERT INTO todos (title, completed) VALUES (?, ?)")
        .bind(&form.title)
        .bind(false)
        .execute(&pool)
        .await
        .unwrap()
        .last_insert_rowid();

    let new_todo = Todo {
        id,
        title: form.title,
        completed: false,
    };

    // 只渲染 todo.html 片段
    TodoTemplate::from(new_todo)
}

// 切换状态：更新 DB，返回更新后的 HTML 片段 (HTMX 替换旧行)
async fn toggle_todo(
    State(pool): State<SqlitePool>,
    Path(id): Path<i64>,
) -> impl IntoResponse {
    // 1. 获取当前状态并取反
    // 注意：实际生产中应使用事务或更严谨的 SQL 更新逻辑
    let todo = sqlx::query_as::<_, Todo>("SELECT * FROM todos WHERE id = ?")
        .bind(id)
        .fetch_one(&pool)
        .await
        .unwrap();

    let new_completed = !todo.completed;

    sqlx::query("UPDATE todos SET completed = ? WHERE id = ?")
        .bind(new_completed)
        .bind(id)
        .execute(&pool)
        .await
        .unwrap();

    let updated_todo = Todo {
        completed: new_completed,
        ..todo
    };

    TodoTemplate::from(updated_todo)
}

// 删除：从 DB 删除，返回空内容 (HTMX 会移除对应的 DOM 元素)
async fn delete_todo(
    State(pool): State<SqlitePool>,
    Path(id): Path<i64>,
) -> impl IntoResponse {
    sqlx::query("DELETE FROM todos WHERE id = ?")
        .bind(id)
        .execute(&pool)
        .await
        .unwrap();

    // 返回 OK 状态码，不返回内容，HTMX 接收到后会根据 hx-swap="outerHTML" 移除元素
    StatusCode::OK
}

// --- 4. 主函数 ---

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // 创建数据库连接池
    let database_url = "sqlite://todos.db?mode=rwc"; // mode=rwc 允许自动创建文件
    let pool = SqlitePoolOptions::new()
        .max_connections(5)
        .connect(database_url)
        .await?;

    // 初始化数据库表
    sqlx::query(
        "CREATE TABLE IF NOT EXISTS todos (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT 0
        )",
    )
    .execute(&pool)
    .await?;

    // 构建路由
    let app = Router::new()
        .route("/", get(index))
        .route("/todos", post(add_todo))
        .route("/todos/:id", get(toggle_todo).put(toggle_todo).delete(delete_todo)) // 兼容 HTMX 常用方法
        .with_state(pool);

    // 启动服务器
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("监听中: http://{}", addr);

    let listener = tokio::net::TcpListener::bind(addr).await?;
    axum::serve(listener, app).await?;

    Ok(())
}
